package com.fanshuai.soring.boot.webflux.log;


import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Objects;

@Slf4j
public class SimpleLogFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        if (!(request instanceof ServerHttpRequestDecorator)) {
            ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(request);
            exchange = exchange.mutate().request(requestDecorator).build();
        }
        if (!(response instanceof ServerHttpResponseDecorator)) {
            ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(response);
            exchange = exchange.mutate().response(responseDecorator).build();
        }

        try {
            if (shouldLog(exchange)) {
                beforeRequest(exchange);
            }
            return chain.filter(exchange);
        } finally {
            if (shouldLog(exchange)) {
                afterRequest(exchange);
            }
        }
    }

    private boolean shouldLog(ServerWebExchange exchange) {
        String path = exchange.getRequest().getURI().getPath();
        return !path.endsWith(".icon") && !path.endsWith(".html");
    }

    private void beforeRequest(ServerWebExchange exchange) {
        ServerHttpRequestDecorator requestDecorator = (ServerHttpRequestDecorator) exchange.getRequest();

        StringBuilder b = new StringBuilder();
        b.append(Objects.requireNonNull(requestDecorator.getMethod()).name()).append(" ").append(requestDecorator.getPath());
        b.append("?").append(requestDecorator.getQueryParams());

        b.append(" headers=").append(requestDecorator.getHeaders());
        b.append(" body=");
        requestDecorator.getBody().next().switchIfEmpty(Mono.empty()).subscribe(buffer -> {
            b.append(buffer.toString(StandardCharsets.UTF_8));
        });
        log.info("request={}", b);
    }

    private void afterRequest(ServerWebExchange exchange) {
        ServerHttpResponseDecorator responseDecorator = (ServerHttpResponseDecorator) exchange.getResponse();
        StringBuilder b = new StringBuilder();

        b.append(responseDecorator.getStatusCode()).append("\n");
        b.append(" headers=").append(responseDecorator.getHeaders());
        log.info("response={}", b);
    }
}
